/*
Copyright 2007 Luke Benstead

This file is part of slMath.

slMath is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

slMath is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.

You should have received a copy of the GNU Lesser Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#ifndef VEC3_H_INCLUDED
#define VEC3_H_INCLUDED

#include <cassert>

#ifdef USE_DOUBLE_PRECISION
	typedef double slScalar;
#else
	typedef float slScalar;
#endif

struct slMat4;
struct slVec3;

slScalar slVec3Length(const slVec3* pIn); ///< Returns the length of the vector
slScalar slVec3LengthSq(const slVec3* pIn); ///< Returns the square of the length of the vector
slVec3* slVec3Normalize(slVec3* pOut, const slVec3* pIn); ///< Returns the vector passed in set to unit length
slVec3* slVec3Cross(slVec3* pOut, const slVec3* pV1, const slVec3* pV2); ///< Returns a vector perpendicular to 2 other vectors
slScalar slVec3Dot(const slVec3* pV1, const slVec3* pV2); ///< Returns the cosine of the angle between 2 vectors
slVec3* slVec3Add(slVec3* pOut, const slVec3* pV1, const slVec3* pV2); ///< Adds 2 vectors and returns the result
slVec3* slVec3Subtract(slVec3* pOut, const slVec3* pV1, const slVec3* pV2); ///< Subtracts 2 vectors and returns the result
slVec3* slVec3Transform(slVec3* pOut, const slVec3* pV1, const slMat4* pM);
slVec3* slVec3TransformNormal(slVec3* pOut, const slVec3* pV, const slMat4* pM);
slVec3* slVec3TransformCoord(slVec3* pOut, const slVec3* pV, const slMat4* pM); ///<Transforms a 3D vector by a given matrix, projecting the result back into w = 1.
slVec3* slVec3Scale(slVec3* pOut, const slVec3* pIn, const slScalar s); ///< Scales a vector to length s
bool 	slVec3AreEqual(const slVec3* p1, const slVec3* p2);
slVec3* slVec3InverseTransform(slVec3* pOut, const slVec3* pV, const slMat4* pM);
slVec3* slVec3InverseTransformNormal(slVec3* pOut, const slVec3* pVect, const slMat4* pM);

struct slVec3 {
	slScalar m_X;
	slScalar m_Y;
	slScalar m_Z;

	slVec3(slScalar x, slScalar y, slScalar z);
	slVec3();
	
	slVec3(const slVec3& vec) {
		m_X = vec.m_X;
		m_Y = vec.m_Y;
		m_Z = vec.m_Z;
	}

	slVec3& operator= (const slVec3& vec) {
		m_X = vec.m_X;
		m_Y = vec.m_Y;
		m_Z = vec.m_Z;
		return *this;
	}

	inline slScalar Length() {
		return slVec3Length(this);
	}

	// casting
	operator slScalar* () {
		return reinterpret_cast<slScalar*> (this);
	}

	operator const slScalar* () const {
		return reinterpret_cast<const slScalar*> (this);
	}

	slVec3& operator+= (const slVec3& vec) {
		slVec3Add(this, this, &vec);
		return *this;
	}

	slVec3& operator-= (const slVec3& vec) {
		slVec3Subtract(this, this, &vec);
		return *this;
	}

	slVec3& operator*= (slScalar s) {
		slVec3Scale(this, this, s);
		return *this;
	}

	slVec3& operator/= (slScalar s) {
		s = 1.0f / s;
		m_X *= s;
		m_Y *= s;
		m_Z *= s;

		return *this;
	}

	const slVec3 operator- () const {
		return slVec3(-m_X, -m_Y, -m_Z);
	}

	const slVec3 operator+ (const slVec3& vec) const {
		return slVec3(	m_X + vec.m_X,
						m_Y + vec.m_Y,
						m_Z + vec.m_Z  );
	}

	const slVec3 operator- (const slVec3& vec) const {
		return slVec3(	m_X - vec.m_X,
						m_Y - vec.m_Y,
						m_Z - vec.m_Z  );
	}

	const slVec3 operator* (slScalar s) const {
		return slVec3(	m_X * s,
						m_Y * s,
						m_Z * s  );
	}

	const slVec3 operator/ (slScalar s) const {
		s = 1.0f / s;
		return slVec3(	m_X * s,
						m_Y * s,
						m_Z * s  );
	}

	bool operator== (const slVec3& vec) const {
		return slVec3AreEqual(this, &vec);
	}

	bool operator!= (const slVec3& vec) const {
		return !slVec3AreEqual(this, &vec);
	}

	const slVec3 Cross(const slVec3& vec) const {
		slVec3 ret;
		slVec3Cross(&ret, this, &vec);
		return ret;
	}

	const slScalar Dot(const slVec3& vec) const {
		return slVec3Dot(this, &vec);
	}
};


#endif // VEC3_H_INCLUDED
